#ifndef RTAPI_PROC_H
#define RTAPI_PROC_H
#if defined(CONFIG_PROC_FS) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)

#define RTAPI_USE_PROCFS

#include "procfs_macros.h" /* macros for read functions */

struct proc_dir_entry* rtapi_dir = 0;           /* /proc/rtapi directory */
static struct proc_dir_entry* status_file = 0;  /* /proc/rtapi/status */
static struct proc_dir_entry* modules_file = 0; /* /proc/rtapi/modules */
static struct proc_dir_entry* tasks_file = 0;   /* /proc/rtapi/tasks */
static struct proc_dir_entry* shmem_file = 0;   /* /proc/rtapi/shmem */
static struct proc_dir_entry* sems_file = 0;    /* /proc/rtapi/sems */
static struct proc_dir_entry* fifos_file = 0;   /* /proc/rtapi/fifos */
static struct proc_dir_entry* debug_file = 0;   /* /proc/rtapi/debug */

/** The following are callback functions for the /proc filesystem
    When someone reads a /proc file, the appropriate function below
    is called, and it must generate output for the reader on the fly.
    These functions use the MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT
    macros to make sure the RTAPI module is not removed while servicing
    a /proc request.
*/

static int proc_read_status(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    PROC_PRINT_VARS;
    PROC_PRINT("******* RTAPI STATUS ********\n");
    PROC_PRINT("   RT Modules = %i\n", rtapi_data->rt_module_count);
    PROC_PRINT("   UL Modules = %i\n", rtapi_data->ul_module_count);
    PROC_PRINT("        Tasks = %i/%i\n", rtapi_data->task_count, RTAPI_MAX_TASKS);
    PROC_PRINT("Shared memory = %i/%i\n", rtapi_data->shmem_count, RTAPI_MAX_SHMEMS);
    PROC_PRINT("        FIFOs = %i/%i\n", rtapi_data->fifo_count, RTAPI_MAX_FIFOS);
    PROC_PRINT("   Semaphores = %i/%i\n", rtapi_data->sem_count, RTAPI_MAX_SEMS);
    PROC_PRINT("   Interrupts = %i\n", rtapi_data->irq_count);
    PROC_PRINT("  RT task CPU = %i\n", rtapi_data->rt_cpu);
    if (rtapi_data->timer_running)
    {
        PROC_PRINT(" Timer status = Running\n");
        PROC_PRINT(" Timer period = %li nSec\n", rtapi_data->timer_period);
    }
    else
    {
        PROC_PRINT(" Timer status = Stopped\n");
    }
    PROC_PRINT("Message level = %i\n", msg_level);
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_read_modules(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    int n;
    char* state_str;

    PROC_PRINT_VARS;
    PROC_PRINT("******* RTAPI MODULES *******\n");
    PROC_PRINT("ID  Type  Name\n");
    for (n = 1; n <= RTAPI_MAX_MODULES; n++)
    {
        if (module_array[n].state != NO_MODULE)
        {
            switch (module_array[n].state)
            {
            case REALTIME:
                state_str = "RT  ";
                break;
            case USERSPACE:
                state_str = "USER";
                break;
            default:
                state_str = "????";
                break;
            }
            PROC_PRINT("%02d  %s  %s\n", n, state_str, module_array[n].name);
        }
    }
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_read_tasks(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    int n;
    char* state_str;

    PROC_PRINT_VARS;
    PROC_PRINT("******** RTAPI TASKS ********\n");
    PROC_PRINT("ID  Own  Prio  State     Code\n");
    for (n = 1; n <= RTAPI_MAX_TASKS; n++)
    {
        if (task_array[n].state != EMPTY)
        {
            switch (task_array[n].state)
            {
            case PAUSED:
                state_str = "PAUSED  ";
                break;
            case PERIODIC:
                state_str = "PERIODIC";
                break;
            case FREERUN:
                state_str = "FREE RUN";
                break;
            case ENDED:
                state_str = "ENDED   ";
                break;
            default:
                state_str = "UNKNOWN ";
                break;
            }
            PROC_PRINT("%02d  %02d   %3d   %s  %p\n", n, task_array[n].owner, task_array[n].prio, state_str, task_array[n].taskcode);
        }
    }
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_read_shmem(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    int n;

    PROC_PRINT_VARS;
    PROC_PRINT("**** RTAPI SHARED MEMORY ****\n");
    PROC_PRINT("ID  Users  Key         Size\n");
    PROC_PRINT("    RT/UL                  \n");
    for (n = 1; n <= RTAPI_MAX_SHMEMS; n++)
    {
        if (shmem_array[n].key != 0)
        {
            PROC_PRINT("%02d  %2d/%-2d  %-10d  %-10lu\n", n, shmem_array[n].rtusers, shmem_array[n].ulusers, shmem_array[n].key, shmem_array[n].size);
        }
    }
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_read_sems(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    int n;

    PROC_PRINT_VARS;
    PROC_PRINT("***** RTAPI SEMAPHORES ******\n");
    PROC_PRINT("ID  Users  Key\n");
    for (n = 1; n <= RTAPI_MAX_SEMS; n++)
    {
        if (sem_array[n].users != 0)
        {
            PROC_PRINT("%02d  %3d    %-10d\n", n, sem_array[n].users, sem_array[n].key);
        }
    }
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_read_fifos(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    int n;
    char* state_str;

    PROC_PRINT_VARS;
    PROC_PRINT("******** RTAPI FIFOS ********\n");
    PROC_PRINT("ID  State  Key         Size\n");
    for (n = 1; n <= RTAPI_MAX_FIFOS; n++)
    {
        if (fifo_array[n].state != UNUSED)
        {
            switch (fifo_array[n].state)
            {
            case HAS_READER:
                state_str = "R-";
                break;
            case HAS_WRITER:
                state_str = "-W";
                break;
            case HAS_BOTH:
                state_str = "RW";
                break;
            default:
                state_str = "UNKNOWN ";
                break;
            }
            PROC_PRINT("%02d   %s    %-10d  %-10ld\n", n, state_str, fifo_array[n].key, fifo_array[n].size);
        }
    }
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_read_debug(char* page, char** start, off_t off, int count, int* eof, void* data)
{
    PROC_PRINT_VARS;
    PROC_PRINT("******* RTAPI MESSAGES ******\n");
    PROC_PRINT("  Message Level  = %i\n", msg_level);
    PROC_PRINT("  ERROR messages = %s\n", msg_level >= RTAPI_MSG_ERR ? "ON" : "OFF");
    PROC_PRINT("WARNING messages = %s\n", msg_level >= RTAPI_MSG_WARN ? "ON" : "OFF");
    PROC_PRINT("   INFO messages = %s\n", msg_level >= RTAPI_MSG_INFO ? "ON" : "OFF");
    PROC_PRINT("  DEBUG messages = %s\n", msg_level >= RTAPI_MSG_DBG ? "ON" : "OFF");
    PROC_PRINT("\n");
    PROC_PRINT_DONE;
}

static int proc_write_debug(struct file* file, const char* buffer, unsigned long count, void* data)
{
    char c;

    /* copy 1 byte from user space */
    if (copy_from_user(&c, buffer, 1))
    {
        return -1;
    }
    /* check it is a digit */
    if (isdigit(c))
    {
        /* convert to a number */
        msg_level = (int)(c - '0');
        /* cap the value if it is outside the valid range */
        if (msg_level < RTAPI_MSG_NONE)
        {
            msg_level = RTAPI_MSG_NONE;
        }
        if (msg_level > RTAPI_MSG_ALL)
        {
            msg_level = RTAPI_MSG_ALL;
        }
    }
    /* tell whoever called us that we used all the data, even though we
       really only used the first byte */
    return count;
}

/** proc_init() initializes the /proc filesystem entries,
    creating the directory and files, and linking them
    to the appropriate callback functions. This function
    is called from the init_module() function of the
    RTAPI implementation.
*/

static int proc_init(void)
{
    /* create the rtapi directory "/proc/rtapi" */
    rtapi_dir = create_proc_entry("rtapi", S_IFDIR, NULL);
    if (rtapi_dir == 0)
    {
        return -1;
    }

    /* create read only file "/proc/rtapi/status" */
    status_file = create_proc_entry("status", S_IRUGO, rtapi_dir);
    if (status_file == NULL)
    {
        return -1;
    }
    status_file->read_proc = proc_read_status;

    /* create read only file "/proc/rtapi/modules" */
    modules_file = create_proc_entry("modules", S_IRUGO, rtapi_dir);
    if (modules_file == NULL)
    {
        return -1;
    }
    modules_file->read_proc = proc_read_modules;

    /* create read only file "/proc/rtapi/tasks" */
    tasks_file = create_proc_entry("tasks", S_IRUGO, rtapi_dir);
    if (tasks_file == NULL)
    {
        return -1;
    }
    tasks_file->read_proc = proc_read_tasks;

    /* create read only file "/proc/rtapi/shmem" */
    shmem_file = create_proc_entry("shmem", S_IRUGO, rtapi_dir);
    if (shmem_file == NULL)
    {
        return -1;
    }
    shmem_file->read_proc = proc_read_shmem;

    /* create read only file "/proc/rtapi/sems" */
    sems_file = create_proc_entry("sems", S_IRUGO, rtapi_dir);
    if (sems_file == NULL)
    {
        return -1;
    }
    sems_file->read_proc = proc_read_sems;

    /* create read only file "/proc/rtapi/fifos" */
    fifos_file = create_proc_entry("fifos", S_IRUGO, rtapi_dir);
    if (fifos_file == NULL)
    {
        return -1;
    }
    fifos_file->read_proc = proc_read_fifos;

    /* create read/write file "/proc/rtapi/debug" */
    debug_file = create_proc_entry("debug", S_IRUGO | S_IWUGO, rtapi_dir);
    if (debug_file == NULL)
    {
        return -1;
    }
    debug_file->data = NULL;
    debug_file->read_proc = proc_read_debug;
    debug_file->write_proc = proc_write_debug;
    return 0;
}

/** proc_clean() is called from the cleanup_module function of
    of the RTAPI implementation.  It removes the rtapi entries
    from the /proc filesystem.  Failing to remove a /proc
    entry before the module is removed may cause kernel panics.
*/

static void proc_clean(void)
{
    /* remove /proc entries, only if they exist */
    if (rtapi_dir != NULL)
    {
        if (status_file != NULL)
        {
            remove_proc_entry("status", rtapi_dir);
            status_file = NULL;
        }
        if (modules_file != NULL)
        {
            remove_proc_entry("modules", rtapi_dir);
            modules_file = NULL;
        }
        if (tasks_file != NULL)
        {
            remove_proc_entry("tasks", rtapi_dir);
            tasks_file = NULL;
        }
        if (shmem_file != NULL)
        {
            remove_proc_entry("shmem", rtapi_dir);
            shmem_file = NULL;
        }
        if (sems_file != NULL)
        {
            remove_proc_entry("sems", rtapi_dir);
            sems_file = NULL;
        }
        if (fifos_file != NULL)
        {
            remove_proc_entry("fifos", rtapi_dir);
            fifos_file = NULL;
        }
        if (debug_file != NULL)
        {
            remove_proc_entry("debug", rtapi_dir);
            debug_file = NULL;
        }
        remove_proc_entry("rtapi", NULL);
    }
}

EXPORT_SYMBOL(rtapi_dir);

#endif /* CONFIG_PROC_FS */
#endif /* RTAPI_PROC_H */
